package com.ramnight.sql2.component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import com.ramnight.sql2.core.DB;
import com.ramnight.sql2.core.Sql2;
import com.ramnight.sql2.core.TABLE;
import com.ramnight.sql2.util.SqlKit;

public class Query implements Cloneable {
	private String select = "*";
	private String table;
	private String tableAs;
	//private String join;
	private HashMap<String, String> joinMap;
	private String groupBy;
	private ArrayList<Object> param = null;
	private Where where = new Where();
	private String order;
	private String limit;
	
	Query(){
	}
	
	Query(Model model){
		this.table = model.table();
		this.where(new Where(model.map()));
	}
	
	Query(String table){
		this.table = table;
	}
	
	Query(String table, String as){
		this.table = table;
		this.tableAs = as;
	}
	
	Query(String table, Object pk){
		this.table = table;
		this.where = where.eq(Sql2.getPk(), pk);
	}
	
	public Query join(String table, String as, String on){
		if(joinMap == null){
			joinMap = new HashMap<>();
		}
		joinMap.put(table, " left join " + SqlKit.wrapCol(table) + " " + as + " on " + on);
		return this;
	}
	
	/**
	 * 自动去除非数据库字段
	 */
	public Query cols(String...cols){
		Set<String> colSet = getColSet();
		for(String col : cols){
			if(colSet.contains(col)){
				this.selectSql(SqlKit.wrapCol(col));
			}
		}
		return this;
	}
	
	private Set<String> getColSet(){
		Set<String> colSet = TABLE.getCols(table);
		if(joinMap != null){
			for(String key : joinMap.keySet()){
				colSet.addAll(TABLE.getCols(key));
			}
		}
		return colSet;
	}
	
	/**
	 * without check
	 */
	public Query select(String...keys){
		for(String key : keys){
			this.selectSql(key);
		}
		return this;
	}
	
	/**
	 * for 子查询
	 */
	public Query selectSql(String sql){
		return selectSql(sql, null);
	}
	
	public Query selectSql(String sql, String as, Object...values){
		return selectSql(true, sql, as, values);
	}
	
	public Query selectWith(String sql, String as, Object...values){
		return selectSql(false, sql, as, values);
	}
	
	private Query selectSql(boolean removeStar, String sql, String as, Object...values){
		if(as != null){
			sql = SqlKit.as(sql, as);
		}
		
		if("*".equals(this.select) && removeStar){
			this.select = sql;
		}else{
			this.select = SqlKit.concat(this.select, sql);
		}
		
		if(values != null){
			if(this.param == null){
				param = new ArrayList<>();
			}
			param.addAll(Arrays.asList(values));
		}
		
		return this;
	}
	
	public Query where(Where where){
		this.where = where;
		return this;
	}
	
	public Query where(Model model){
		this.where(new Where(model.map()));
		return this;
	}
	
	public Query where(String key, Object value){
		where.eq(key, value);
		return this;
	}
	
	public Query pk(Object pk){
		where.eq(Sql2.getPk(), pk);
		return this;
	} 
	
	public Query limit(int begin, int size){
		limit = " limit " + begin + "," + size + " ";
		return this;
	}
	
	public Query limit(int size){
		limit = " limit " + size + " ";
		return this;
	}
	
	public Query orderAsc(String col){
		return order(col, "asc");
	}
	
	public Query orderDesc(String col){
		return order(col, "desc");
	}
	
	public Query order(String col, String sort){
		return order(SqlKit.wrapCol(col) + " " + sort);
	}
	
	public Query orderField(String col, String... values){
		StringBuilder sb = new StringBuilder();
		sb.append("field(").append(SqlKit.wrapCol(col))
			.append(SqlKit.concatString(values)).append(")");
		return order(sb.toString());
	}
	
	private Query order(String str){
		if(order == null){
			order = " order by ";
		}else{
			order += ",";
		}
		order += str;
		return this;
	}
	
	public Query groupBy(String col){
		this.groupBy = SqlKit.wrapCol(col);
		return this;
	}
	
	public String sql(){
		StringBuilder sql = new StringBuilder();
		sql.append("select ").append(select).append(" from ").append(SqlKit.wrapCol(table))
			.append(tableAs == null ? "" : " " + tableAs)
			.append(joinMap == null ? "" : generateJoin())
			.append(where.sql())
			.append(groupBy == null ? "" : " group by " + groupBy)
			.append(order == null ? "" : order)
			.append(limit == null ? "" : limit);
		return sql.toString();
	}
	
	private String generateJoin(){
		StringBuilder sb = new StringBuilder();
		for(Entry<String, String> entry : joinMap.entrySet()){
			sb.append(entry.getValue());
		}
		return sb.toString();
	}
	
	public Object[] param(){
		if(this.param != null){
			return SqlKit.join(this.param, where.param());
		}
		return where.param().toArray();
	}
	
	public String table(){
		return table;
	}
	
	public Query clone(){
		try {
			return (Query)super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
	}
	
	public int count(){
		return DB.count(this.selectSql("count(1)").sql(), this.param());
	}
	
	public boolean exist(){
		return count() > 0;
	}
	
	public Record find(){
		return DB.find(this.sql(), this.param());
	}
	
	public <T> T findCol(){
		return DB.findCol(this.sql(), this.param());
	}
	
	public <T> T find(String select){
		return this.selectSql(select).findCol();
	}
	
	public Long findId(){
		this.select = SqlKit.wrapCol(Sql2.getPk());
		return findCol();
	}
	
	public RecList search(){
		return DB.search(this.sql(), this.param());
	}
	
	public <T> List<T> searchPks(){
		this.select = SqlKit.wrapCol(Sql2.getPk());
		return searchCol();
	}
	
	public <T> List<T> searchCol(){
		return DB.searchCol(this.sql(), this.param());
	}
	
	public <T> List<T> search(String select){
		this.selectSql(select);
		return DB.searchCol(this.sql(), this.param());
	}
	
	public Page paginate(int pageNumber, int pageSize){
		return DB.paginate(this.sql(), pageNumber, pageSize, this.param());
	}
}
